/*
 * Android-PercentProgressBar
 * Copyright (c) 2015  Natasa Misic
 *
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package com.natasa.progresspercent.lib;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;

import java.util.Locale;

/**
 * 圆形进度
 *
 * @since 2021-07-15
 */
public class CircularProgress extends BaseProgressView implements Component.DrawTask, Component.EstimateSizeListener {
    private static final float ANGLE_360 = 360;
    private static final float DEFAULT_NUMBER = 2f;
    private static final float ANGLE_180 = 180f;
    private static final int ANG_TXT_MARGIN = 5;
    private static final int START_ANGLE0 = -80;
    private static final int START_ANGLE1 = -92;
    private static final int CIRCLE_TXT_PADDING = 30;
    private RectFloat rectF;
    private float angle;
    private int min;
    private Arc bgArc = new Arc();
    private Arc groundArc = new Arc();

    /**
     * 构造
     *
     * @param context 上下文
     */
    public CircularProgress(Context context) {
        super(context);
    }

    /**
     * 构造
     *
     * @param context 上下文
     * @param attrSet 属性集合
     */
    public CircularProgress(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

    /**
     * 构造
     *
     * @param context 上下文
     * @param attrSet 属性集合
     * @param styleName 样式
     */
    public CircularProgress(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
    }

    @Override
    protected void init(Context ctx) {
        mContext = ctx;
        rectF = new RectFloat();
        setProgress(0);
        initBackgroundColor();
        initForegroundColor();
        initTextColor();
        addDrawTask(this);
        setEstimateSizeListener(this);
    }

    @Override
    protected void initBackgroundColor() {
        super.initBackgroundColor();
    }

    @Override
    protected void initForegroundColor() {
        super.initForegroundColor();
    }

    @Override
    protected void initTextColor() {
        super.initTextColor();
    }

    @Override
    public synchronized void onDraw(Component component, Canvas canvas) {
        angle = ANGLE_360 * progress / maxProgress;
        bgArc.setArc(START_ANGLE0, angle - ANGLE_360, false);
        canvas.drawArc(rectF, bgArc, backgroundPaint);
        if (progress > 1) {
            groundArc.setArc(START_ANGLE1, angle, false);
            canvas.drawArc(rectF, groundArc, foregroundPaint);
        }
        drawText(canvas);
    }

    private void drawText(Canvas canvas) {
        float angleX = (float) ((angle + 1.5) * Math.PI / ANGLE_180);
        float angleY = (float) ((angle + DEFAULT_NUMBER) * Math.PI / ANGLE_180);
        float startX = (float) (min / DEFAULT_NUMBER - ANG_TXT_MARGIN + rectF.getWidth()
            / DEFAULT_NUMBER * Math.sin(angleX));
        float startY = (float) (min / DEFAULT_NUMBER + ANG_TXT_MARGIN - rectF.getWidth()
            / DEFAULT_NUMBER * Math.cos(angleY));
        float txtMarginX;
        float txtMarginY;
        if (progress > 98) {
            canvas.save();
            txtMarginX = 30;
            txtMarginY = -10;
            canvas.rotate(angle, startX, startY);
            canvas.drawText(textPaint, String.format(Locale.ENGLISH, "%d%%", progress),
                startX - txtMarginX, startY + txtMarginY);
            canvas.restore();
        } else {
            txtMarginX = 18;
            canvas.drawText(textPaint, String.format(Locale.ENGLISH, "%d%%", progress),
                startX - txtMarginX, startY + ANG_TXT_MARGIN);
        }
    }

    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        min = setDimensions(widthMeasureSpec, heightMeasureSpec);
        float left = 0 + strokeWidth / DEFAULT_NUMBER;
        float top = 0 + strokeWidth / DEFAULT_NUMBER;
        float right = min - strokeWidth / DEFAULT_NUMBER;
        float bottom = min - strokeWidth / DEFAULT_NUMBER;
        rectF.left = left + padding + CIRCLE_TXT_PADDING;
        rectF.top = top + padding + CIRCLE_TXT_PADDING;
        rectF.right = right - padding - CIRCLE_TXT_PADDING;
        rectF.bottom = bottom - padding - CIRCLE_TXT_PADDING;
        return true;
    }

    private int setDimensions(int widthMeasureSpec, int heightMeasureSpec) {
        height = getDefaultSize(getMinHeight(), heightMeasureSpec);
        width = getDefaultSize(getMinHeight(), widthMeasureSpec);
        int specMode = EstimateSpec.getMode(widthMeasureSpec);
        final int smallerDimens = Math.min(width, height);
        setEstimatedSize(EstimateSpec.getChildSizeWithMode(smallerDimens, smallerDimens, specMode),
            EstimateSpec.getChildSizeWithMode(smallerDimens, smallerDimens, specMode));
        return smallerDimens;
    }

    /**
     * Utility to return a default size. Uses the supplied size if the
     * MeasureSpec imposed no constraints. Will get larger if allowed
     * by the MeasureSpec.
     *
     * @param size Default size for this view
     * @param measureSpec Constraints imposed by the parent
     * @return The size this view should be.
     */
    private int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = EstimateSpec.getMode(measureSpec);
        int specSize = EstimateSpec.getSize(measureSpec);
        switch (specMode) {
            case EstimateSpec.NOT_EXCEED:
            case EstimateSpec.PRECISE:
                result = specSize;
                break;
            default:
                break;
        }
        return result;
    }
}
